// ---------------------------------1-----------------------------------
// 针对属性一样的东西 在ts中，会认为是一样的。 ts中的类型检测叫鸭子类型检测
class Cat {
  constructor(name: string, age: number) {}
}
class Dog {
  constructor(name: string, age: number) {}
}
// 1.类本身也是一个类型 只能描述梳理
// 2.这个变量就是类本身 我们可以把类型取出来使用 类本身的类型取出来

// ts中的类型标准并不会让代码执行, 静态检测
// type MyClazz<T> = {new (name:string,age:number):T}
type MyClazz<T> = new (name: string, age: number) => T; // 类型描述 不是真实的new 表示这个变量能被new
// interface MyClazz<T>  {
//     // new xxx 描述的是构造函数能被new : 返回的具体类型
//     new (name:string,age:number):T
// }
function createAnimal<T>(clazz: MyClazz<T>, name: string, age: number) {
  return new clazz(name, age);
}
// Cat 描述的是实例
// typeof Cat 描述的是类本身
let r = createAnimal(Cat, "Tom", 12);
// ---------------------------------2----------------------------------
// 创建一个数组 根据长度 和内容 创建一个数组  3 'a'   ['a','a','a']   3 1 => [1,1,1]
/** 
interface ICreateArray {
  <T>(times: number, val: T): T[];
}
// type createArray1  =  <T>(times:number,val:T)=>T[]
const createArray: ICreateArray = (times, val) => { ，
  let result = [];
  for (let i = 0; i < times; i++) {
    result.push(val);
  }
  return result;
};
let arr = createArray(3, "a");
*/
interface ICreateArray {
  //  写在接口后面表示使用接口的时候确定类型
  <T>(times: number, val: T): T[]; //  // 写在函数前面当赋予方法的时候才能确定类型
}
const createArray: ICreateArray = (times, val) => {
  let result = [];
  for (let i = 0; i < times; i++) {
    result.push(val);
  }
  return result;
};
let arr = createArray(3, "a");
// ---------------------------------3----------------------------------
// 多个泛型
function swap<T, K>(tuple: [T, K]): [K, T] {
  // 声明多个参数来使用
  return [tuple[1], tuple[0]];
}

let r1 = swap(["a", 1]); // [string,number] => [number,string]
type MySwap = typeof r1;

// -----------------------------44---------------------------------
// 如果听过node 就参加webpack课
// 如果没听过node  和我听node课  下周主要讲vue3的应用  实战 源码

// 让方法本身帮你标识回调函数
interface ICb<T> {
  (item: T, key: number): void;
}
interface IForEach {
  <T>(arr: T[], cb: ICb<T>): void;
}
let forEach: IForEach = (arr, cb) => {
  for (let i = 0; i < arr.length; i++) {
    cb(arr[i], i);
  }
};
forEach([1, 2, 3, 4, 5, "string"], (item) => {});

// ---------------------------------55---------------------------------
// 泛型约束 默认泛型之间是不能计算的

// 约束泛型T 是具备string 特性的 

// 约束的概念 就是要求你得具有他的特性
function sum<T extends  number|string>(a: T, b: T) { // 两个泛型间不具备相加的能力
    // 把多个类型断言成 一个类型  string | boolean | xxx
    return a as string +b; // 不能保证两个泛型相加后的结果 ， 考虑安全性  string + number|string = ?
}
let rr1= sum(1,2); // 我想加的两个数 类型就是一样的

// 约束的含义就是具备他的功能

// 约束只要满足特性就可以
// 我们可以通过 一些标识来限制对象   

function getFish<T extends {kind:string}>(fish:T){}
getFish({a:1,kind:'鲨鱼'}); // 对传入的参数进行了类型校验
// 我要求必须要有类型 

let obj = {name:'zf',age:12}
type xx = keyof typeof obj; // keyof 后面根的是ts中类型
type MyType = keyof number
type MyAny = keyof any; // string / number / symbol 只有这三个值能作为key
// 1.标识泛型必须是一个对象       2. 标识key在对象的key中
function getValueFromKey<T extends object,K extends  keyof T>(obj:T,key:K){

    // js....
}
getValueFromKey(obj,'age');

// 联合类型 是交集还是并集 ? 联合类型是并集
// ts 里面 交叉类型 

interface P1{ // 正常想的肯定是type
    handsome:string,
    type:number
}
interface P2{
    high:string,
    type:number
}
type Compute<T> = { // v-for  key in allKey   in  typeof keyof extends 
    [K in keyof T ] : T[K]
}
type P3 = Compute<P1 & P2>

// ts 中的交叉类型 可以理解成交叉的部分 （可以理解成并集 两个人的特点都要有）
// 交完了 范围变小。限制条件变多 

let p3:P3 = {
    handsome:'帅',
    type:123,
    high:'xxx'
}
let p2:P2 = {
    high:'帅',
    type:123
}

// 我可以把p3 赋予给p2吗？  交叉后的结果可以赋予给交叉前的任何一个人
p2=p3;

// 联合类型  并集
let a:string | number;


export {}